package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tbit.Version;
import com.tbit.common.entity.UserOpLog;
import com.tbit.common.entity.view.Result;
import com.tbit.common.utils.Utf8Mb4Checker;
import com.tbit.uqbike.constant.OperaPswConstant;
import com.tbit.uqbike.constant.RefundConstant;
import com.tbit.uqbike.constant.ServiceUrlConstant;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.pojo.AccountUser;
import com.tbit.uqbike.object.pojo.RefundOrder;
import com.tbit.uqbike.object.pojo.User;
import com.tbit.uqbike.object.pojo.dto.*;
import com.tbit.uqbike.object.pojo.vo.RideLogDetailsVO;
import com.tbit.uqbike.object.pojo.vo.RideOrderRefundRpcReqVO;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.service.business.*;
import com.tbit.uqbike.webmanager.business.user_op_log.service.UserOpLogService;
import com.tbit.uqbike.webmanager.dao.log.RefundOrderDao;
import com.tbit.uqbike.webmanager.util.MD5PasswordUtil;
import com.tbit.utils.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author:BUGTian
 * @DATE: 2022/11/28
 */
@Validated
@Service
@Slf4j
public class RefundServiceImpl implements RefundService {

    @Autowired
    private RideLogDetailsService rideLogDetailsService;

    @Autowired
    private AccountUserService accountUserService;
    @Autowired
    private RefundOrderDao refundOrderDao;
    @Autowired
    private UserOpLogService userOpLogService;
    @Autowired
    private UserService userService;
    @Autowired
    private GiftCardUserService giftCardUserService;

    @Override
    public Result<Integer> rideOrderRefund(LoginInfo login, RideOrderRefundDTO rideOrderRefundDTO) {
        Assert.notNulls("必填项不允许为空", rideOrderRefundDTO.getRefundMoney(), rideOrderRefundDTO.getFineMoney(),
                rideOrderRefundDTO.getSendMsg(), rideOrderRefundDTO.getRemark(), rideOrderRefundDTO.getUserId());
        RideLogDetailsVO rideLogDetailsVO = rideLogDetailsService.queryByOrderId(rideOrderRefundDTO.getOrderNO());
        if (rideLogDetailsVO == null) {
            return Result.error("订单不存在");
        }
        if ((int) (NumberUtil.mul(100D, rideLogDetailsVO.getReturnableRideMoney().doubleValue())) < rideOrderRefundDTO.getRefundMoney()) {
            return Result.error("骑行费用退款金额大于剩余可退金额");
        }
        if ((int) (NumberUtil.mul(100D, rideLogDetailsVO.getReturnableFineMoney().doubleValue())) < rideOrderRefundDTO.getFineMoney()) {
            return Result.error("罚款费用退款金额大于剩余可退金额");
        }
        if (rideOrderRefundDTO.getGiftMoney() != null && (int)(NumberUtil.mul(100D,rideLogDetailsVO.getReturnableGiftCardMoney().doubleValue())) < rideOrderRefundDTO.getGiftMoney()) {
            return Result.error("礼品金退款金额大于剩余可退金额");
        }
        //金额不能为0，总退款金额不能小于等于0
        //兼容旧版
        if (rideOrderRefundDTO.getGiftMoney() == null) {
            rideOrderRefundDTO.setGiftMoney(0L);
        }
        long totalRefundMoney = rideOrderRefundDTO.getRefundMoney() + rideOrderRefundDTO.getFineMoney() + rideOrderRefundDTO.getGiftMoney().intValue();
        if (rideOrderRefundDTO.getRefundMoney() < 0 || rideOrderRefundDTO.getFineMoney() < 0 || totalRefundMoney <= 0) {
            return Result.error("退款金额有误");
        }

        /*退礼品卡*/
        if (rideOrderRefundDTO.getGiftMoney().intValue() > 0){
            giftCardUserService.recharge(rideLogDetailsVO.getAccountId(), rideOrderRefundDTO.getUserId(),
                    rideOrderRefundDTO.getGiftMoney().intValue(), rideOrderRefundDTO.getOrderNO(), login);
        }

        if (rideOrderRefundDTO.getRefundMoney() + rideOrderRefundDTO.getFineMoney() > 0){
            RefundDTO refundDTO = new RefundDTO();
            BeanUtils.copyProperties(rideOrderRefundDTO, refundDTO);
            refundDTO.setAdAccountId(rideLogDetailsVO.getAccountId());
            Result<RefundReturnDTO> result = refundNew(login, refundDTO, RIDE_ORDER);
            if (result.isSuccess()){
                return Result.success();
            }else {
                return Result.error(result.getMsg());
            }
        }
      return Result.success();
    }

    @Override
    public Result<Integer> rideOrderRefund(RideOrderRefundRpcReqVO reqVO) {
        RideOrderRefundDTO rideOrderRefundDTO = new RideOrderRefundDTO();
        BeanUtils.copyProperties(reqVO, rideOrderRefundDTO);

        LoginInfo loginInfo = new LoginInfo();
        AccountUser accountUser = accountUserService.getAccountUserById(reqVO.getAccountUserId());
        BeanUtils.copyProperties(accountUser, loginInfo);

        return rideOrderRefund(loginInfo, rideOrderRefundDTO);
    }

    @Override
    public Result<Integer> orderRefund(LoginInfo login, OrderRefundDTO orderRefundDTO) {
        Assert.notNulls("参数有误", orderRefundDTO.getAdAccountId(), orderRefundDTO.getUserId(),
                orderRefundDTO.getRefundMoney(), orderRefundDTO.getOrderNO());
        if (orderRefundDTO.getRefundMoney() <= 0) {
            return Result.error("退款金额有误");
        }
        RefundDTO refundDTO = new RefundDTO();
        BeanUtils.copyProperties(orderRefundDTO, refundDTO);
        Result<RefundReturnDTO> result = refundNew(login, refundDTO, ORDER);
        if (result.isSuccess()){
            return Result.success();
        }else {
            return Result.error(result.getMsg());
        }
//        return refund(login, refundDTO, ORDER);
    }

    @Override
    public Result<RefundReturnDTO> userMoneyRefund(LoginInfo login, UserMoneyRefundDTO userMoneyRefundDTO) {
        Assert.notNulls("参数有误", userMoneyRefundDTO.getUserId(), userMoneyRefundDTO.getRefundMoney());
        if (userMoneyRefundDTO.getRefundMoney() <= 0) {
            return Result.error("退款金额有误");
        }
        RefundDTO refundDTO = new RefundDTO();
        BeanUtils.copyProperties(userMoneyRefundDTO, refundDTO);
        Result<RefundReturnDTO> result = refundNew(login, refundDTO, USER_MONEY);
        if (!result.unSuccess()) {
            // 插入用户操作日志
            User user = userService.getByUserId(userMoneyRefundDTO.getUserId());
            userOpLogService.insertSingle(UserOpLog.builder()
                    .accountId(user.getAccountId())
                    .opUser(login.getName())
                    .opUserPhone(login.getPhone())
                    .targetUser(user.getName())
                    .targetUserPhone(user.getPhone())
                    .operation("用户余额变动")
                    .opType("退款")
                    .opPlatform(login.getLoginUserPlatform())
                    .opTime(LocalDateTime.now())
                    .detail("变动金额: -" + userMoneyRefundDTO.getRefundMoney() / 100d)
                    .build()
            );
        }
        return result;
    }

    @Override
    public Result<Integer> depositRefund(LoginInfo login, DepositRefundDTO depositRefundDTO) {
        Assert.notNulls("参数有误", depositRefundDTO.getUserId());
        RefundDTO refundDTO = new RefundDTO();
        BeanUtils.copyProperties(depositRefundDTO, refundDTO);
        Result<Integer> result = refund(login, refundDTO, DEPOSIT);
        if (!result.unSuccess()) {
            // 插入用户操作日志
            User user = userService.getByUserId(depositRefundDTO.getUserId());
            userOpLogService.insertSingle(UserOpLog.builder()
                    .accountId(user.getAccountId())
                    .opUser(login.getName())
                    .opUserPhone(login.getPhone())
                    .targetUser(user.getName())
                    .targetUserPhone(user.getPhone())
                    .operation("押金变动")
                    .opType("退押金")
                    .opPlatform(login.getLoginUserPlatform())
                    .opTime(LocalDateTime.now())
                    .build()
            );
        }
        return result;
    }
    /**
     * @description: 查询退款结果
     * @author: mo.shanyong
     * @date: 2024/12/19 10:40
     * @param: refundBusinessNO 退款单号
     * @return: Result<com.tbit.uqbike.object.pojo.dto.RefundReturnDTO>
     */
    @Override
    public Result<RefundReturnDTO> queryRefundResult(String refundBusinessNO) {
        List<RefundOrder> refundOrders = refundOrderDao.getByBusinessNo(refundBusinessNO);
        if (CollectionUtils.isEmpty(refundOrders)){
            return Result.error(RefundConstant.NOT_FIND_REFUND_ORDER_MSG);
        }
        //退款中
        double refundingMoney = refundOrders.stream().filter(refundOrder -> {
            return Objects.equals(refundOrder.getRefundStatus(), RefundConstant.REFUNDIND);
        }).mapToDouble(RefundOrder::getRefundMoney).sum();
        //退款成功
        double successMoney = refundOrders.stream().filter(refundOrder -> {
            return Objects.equals(RefundConstant.REFUND_SUCCESS,refundOrder.getRefundStatus());
        }).mapToDouble(RefundOrder::getRefundMoney).sum();
        //退款失败
        double failMoney = refundOrders.stream().filter(refundOrder -> {
            return Objects.equals(RefundConstant.REFUND_FAIL,refundOrder.getRefundStatus());
        }).mapToDouble(RefundOrder::getRefundMoney).sum();
        // 提取 errorMsg 字段非空的项
        List<String> errorMsgList = refundOrders.stream()
                .filter(order -> StringUtils.isNotBlank(order.getErrorMsg()))
                .map(order -> String.format("退款订单号: %s, 发起退款时间: %s, 退款错误信息: %s",
                        order.getRefundNO(),
                        order.getRefundTime(),
                        order.getErrorMsg()))
                .collect(Collectors.toList());
        //获取支付结果的code
        Integer resultCode = this.getRefundResultCode(refundingMoney);
        RefundReturnDTO refundReturnDTO = new RefundReturnDTO();
        refundReturnDTO.setRefundMoney(successMoney);
        refundReturnDTO.setRefundFailMoney(failMoney);
        refundReturnDTO.setRefundTimeOutMoney(refundingMoney);
        refundReturnDTO.setRefundBusinessNO(refundBusinessNO);
        refundReturnDTO.setRefundCode(resultCode);
        refundReturnDTO.setErrorMsgList(errorMsgList);
        return Result.success(RefundConstant.refundMagMap.get(resultCode),refundReturnDTO);
    }

    /**
     * 退款
     *
     * @param refundDTO 退款实体类
     * @param type      退款类型 1：骑行订单退款  2：骑行卡、会员卡、免押卡、优惠券套餐退款  3：用户余额退款  4：押金退款
     */
    public Result<Integer> refund(LoginInfo login, RefundDTO refundDTO, int type) {
		//调用客户端接口进行退款前判断是否包含特殊字符
        if (Utf8Mb4Checker.containsUtf8mb4Chars(refundDTO.getRemark())) {
            throw new BaseException("退款备注含非法字符，请调整后再进行退款申请");
        }
        //调用客户端退款接口
        String url = OperaPswConstant.ACCOUNT_OPERA_PUSH_URL + ServiceUrlConstant.CLIENT_RETURN_MONEY;
        long date = System.currentTimeMillis();
        String param = "youqu";
        String sign = MD5PasswordUtil.MD5("TBIT" + date + param);
        Map<String, Object> paramMap = new HashMap<>(8);
        paramMap.put("userId", refundDTO.getUserId());
        paramMap.put("operatorId", login.getAccountUserId());
        paramMap.put("operatorName", login.getName());
        paramMap.put("operatorPhone", login.getPhone());
        paramMap.put("date", date);
        paramMap.put("param", param);
        paramMap.put("sign", sign);
        switch (type) {
//            case RIDE_ORDER:
//                //骑行订单退款
//                paramMap.put("adAccountId", refundDTO.getAdAccountId());
//                paramMap.put("money", refundDTO.getRefundMoney());
//                paramMap.put("buyOrderNO", refundDTO.getOrderNO());
//                paramMap.put("fineMoney", refundDTO.getFineMoney());
//                paramMap.put("remark", refundDTO.getRemark());
//                paramMap.put("sendMsg", refundDTO.getSendMsg());
//                break;
//            case ORDER:
//                //骑行卡、会员卡、免押卡、优惠券套餐退款
//                paramMap.put("adAccountId", refundDTO.getAdAccountId());
//                paramMap.put("money", refundDTO.getRefundMoney());
//                paramMap.put("buyOrderNO", refundDTO.getOrderNO());
//                break;
//            case USER_MONEY:
//                //余额退款不需要订单号
//                paramMap.put("money", refundDTO.getRefundMoney());
//                break;
            case DEPOSIT:
                //押金退款,不需要传金额,请求的url要换
                url = OperaPswConstant.ACCOUNT_OPERA_PUSH_URL + ServiceUrlConstant.CLIENT_RETURN_DEPOSIT;
                break;
            default:
                break;
        }
        log.info("=================调用客户端接口退款===================");
        log.info(String.format("请求参数：%s", JSON.toJSONString(paramMap)));
        HttpResponse response;
        try {
            response = HttpRequest.get(url).header("U-APP-VERSION", Version.getVersion()).form(paramMap).execute();
        } catch (Exception e) {
            log.error("退款调用客户端接口失败,报错信息:{}", e.getMessage());
            return Result.error("退款失败");
        }
        JSONObject jsonObject = JSON.parseObject(response.body());
        if (type == DEPOSIT) {
            //押金退款单独处理
            Object ret = jsonObject.get("ret");
            if (ret != null && "1".equals(ret.toString())) {
                //成功
                return Result.success();
            }
            log.error("客户端押金退款异常，请求参数：{}，返回参数：{}", JSON.toJSONString(paramMap), response.body());
            String msg = null;
            if (jsonObject.get("msg") != null) {
                msg = jsonObject.get("msg").toString();
            }
            return Result.error(msg);
        }
        //应退款金额
        long refundMoney = refundDTO.getRefundMoney() + (refundDTO.getFineMoney() == null ? 0L : refundDTO.getFineMoney());
        Object data = jsonObject.get("data");
        log.info(JSON.toJSONString(jsonObject));
        Long isRefundMoney = 0L;
        if (!Objects.isNull(data)) {
            //已退款金额
            isRefundMoney = Double.valueOf(data.toString()).longValue();
        }
        if (!isRefundMoney.equals(refundMoney)) {
            log.error("客户端退款异常，请求参数：{}，返回参数：{}", JSON.toJSONString(paramMap), response.body());
            //已退款金额不等于应退款金额
            String msg = String.format("退款失败,已退款金额为:%s元", String.format("%.2f", ((double) isRefundMoney / 100D)));
            Object resMsg = jsonObject.get("msg");
            if (resMsg != null) {
//                msg += "," + resMsg;
                msg = resMsg.toString();
            }
            return Result.error(msg);
        }
        return Result.success();
    }

    /**
     * 退款
     *
     * @param refundDTO 退款实体类
     * @param type      退款类型 1：骑行订单退款  2：骑行卡、会员卡、免押卡、优惠券套餐退款  3：用户余额退款  4：押金退款
     */
    public Result<RefundReturnDTO> refundNew(LoginInfo login, RefundDTO refundDTO, int type) {

        //调用客户端接口进行退款前判断是否包含特殊字符
        if (Utf8Mb4Checker.containsUtf8mb4Chars(refundDTO.getRemark())) {
            throw new BaseException("退款备注含非法字符，请调整后再进行退款申请");
        }
        //调用客户端退款接口
        String url = OperaPswConstant.ACCOUNT_OPERA_PUSH_URL + ServiceUrlConstant.CLIENT_RETURN_MONEY;
        long date = System.currentTimeMillis();
        String param = "youqu";
        String sign = MD5PasswordUtil.MD5("TBIT" + date + param);
        Map<String, Object> paramMap = new HashMap<>(8);
        paramMap.put("userId", refundDTO.getUserId());
        paramMap.put("operatorId", login.getAccountUserId());
        paramMap.put("operatorName", login.getName());
        paramMap.put("operatorPhone", login.getPhone());
        paramMap.put("date", date);
        paramMap.put("param", param);
        paramMap.put("sign", sign);
        switch (type) {
            case RIDE_ORDER:
                //骑行订单退款
                paramMap.put("adAccountId", refundDTO.getAdAccountId());
                paramMap.put("money", refundDTO.getRefundMoney());
                paramMap.put("buyOrderNO", refundDTO.getOrderNO());
                paramMap.put("fineMoney", refundDTO.getFineMoney());
                paramMap.put("remark", refundDTO.getRemark());
                paramMap.put("sendMsg", refundDTO.getSendMsg());
                break;
            case ORDER:
                //骑行卡、会员卡、免押卡、优惠券套餐退款
                paramMap.put("adAccountId", refundDTO.getAdAccountId());
                paramMap.put("money", refundDTO.getRefundMoney());
                paramMap.put("buyOrderNO", refundDTO.getOrderNO());
                break;
            case USER_MONEY:
                //余额退款不需要订单号
                paramMap.put("money", refundDTO.getRefundMoney());
                break;
            case DEPOSIT:
                //押金退款,不需要传金额,请求的url要换
                url = OperaPswConstant.ACCOUNT_OPERA_PUSH_URL + ServiceUrlConstant.CLIENT_RETURN_DEPOSIT;
                break;
            default:
                break;
        }
        log.info("=================调用客户端接口退款===================");
        log.info(String.format("请求参数：%s", JSON.toJSONString(paramMap)));
        HttpResponse response;
        try {
            response = HttpRequest.get(url).header("U-APP-VERSION", Version.getVersion()).form(paramMap).execute();
        } catch (Exception e) {
            log.error("退款调用客户端接口失败,报错信息:{}", e.getMessage());
            return Result.error("退款失败");
        }
        JSONObject jsonObject = JSON.parseObject(response.body());
        if (type == DEPOSIT) {
            //押金退款单独处理
            Object ret = jsonObject.get("ret");
            if (ret != null && "1".equals(ret.toString())) {
                //成功
                return Result.success();
            }
            log.error("客户端押金退款异常，请求参数：{}，返回参数：{}", JSON.toJSONString(paramMap), response.body());
            String msg = null;
            if (jsonObject.get("msg") != null) {
                msg = jsonObject.get("msg").toString();
            }
            return Result.error(msg);
        }
        //应退款金额
        long refundMoney = refundDTO.getRefundMoney() + (refundDTO.getFineMoney() == null ? 0L : refundDTO.getFineMoney());
        Object data = jsonObject.get("data");
        log.info(JSON.toJSONString(jsonObject));
        RefundReturnDTO refundReturnDTO = null;
        Long isRefundMoney = 0L;
        if (!Objects.isNull(data)) {
            //如果返回参中包含退款总单号，则说明不是退押金，调用的是/user/returnMoneyApiNew.do接口
            refundReturnDTO = data.toString().contains(RefundConstant.REFUND_BUSINESS_NO) ?
                    JSON.parseObject(data.toString(), RefundReturnDTO.class) :
                    new RefundReturnDTO(null,Double.valueOf(data.toString()),null,null,null,null);
            isRefundMoney = refundReturnDTO.getRefundMoney().longValue();
        }
        if (!isRefundMoney.equals(refundMoney)) {
            log.error("客户端退款异常，请求参数：{}，返回参数：{}", JSON.toJSONString(paramMap), response.body());
            //已退款金额不等于应退款金额
            String msg = String.format("退款失败,已退款金额为:%s元", String.format("%.2f", ((double) isRefundMoney / 100D)));
            Object resMsg = jsonObject.get("msg");
            if (resMsg != null) {
//                msg += "," + resMsg;
                msg = resMsg.toString();
            }
            return Result.error(msg);
        }
        return Result.success(refundReturnDTO);
    }

    /**
     * @description: 根据不同的退款情况返回不同的code
     * @author: mo.shanyong
     * @date: 2024/12/19 14:12
     * @param: refundingMoney
     * @return: java.lang.Integer
     */
    private Integer getRefundResultCode(double refundingMoney){
        //只要存在退款中订单，就是退款中
        if (!Objects.equals(refundingMoney,0.0d)){
            return RefundConstant.REFUND_TIME_OUT_CODE;
        }
        return RefundConstant.REFUND_SUCCESS_CODE;

    }

}
